Skip to content

Comments

feat: Add IEEE 754 float16 (binary16) support to Rust runtime#3252

Merged
chaokunyang merged 15 commits intoapache:mainfrom
AshharAhmadKhan:feature/float16-support
Feb 21, 2026
Merged

feat: Add IEEE 754 float16 (binary16) support to Rust runtime#3252
chaokunyang merged 15 commits intoapache:mainfrom
AshharAhmadKhan:feature/float16-support

Conversation

@AshharAhmadKhan
Copy link
Contributor

Summary

This PR implements full IEEE 754 half-precision (binary16) float16 support for the Rust runtime as requested in issue #3207.

Implementation Details

Core Type (fory-core/src/float16.rs)

  • New type: #[repr(transparent)] pub struct float16(u16) - 614 lines
  • IEEE 754 compliant f32 ↔ float16 conversions with proper rounding
  • Round-to-nearest, ties-to-even rounding mode
  • Complete special value handling: NaN (payload preservation), ±Inf, ±0, subnormals
  • Overflow/underflow: Overflow → Infinity, Underflow → Subnormal/Zero
  • Policy A comparison: Bitwise Eq/Hash (usable in HashMap) + separate IEEE helpers
  • Arithmetic: Implemented via f32 round-back
  • Classification methods: is_nan, is_infinite, is_finite, is_normal, is_subnormal, is_zero, is_sign_negative
  • Operator traits: Add, Sub, Mul, Div, Neg, PartialOrd
  • Display/Debug: Format via to_f32()

Integration

  • Buffer methods (buffer.rs): write_f16() and read_f16() using little-endian
  • Serializer (serializer/number.rs): Full Serializer trait + ForyDefault implementation
  • Type system (types.rs): Added to BASIC_TYPES, PRIMITIVE_TYPES, PRIMITIVE_ARRAY_TYPES, BASIC_TYPE_NAMES, PRIMITIVE_ARRAY_TYPE_MAP, is_primitive_type_id()
  • Module export (lib.rs): Public module with Float16 alias

Testing

  • 11 comprehensive unit tests covering all edge cases (ALL PASSING)
  • Full test suite (135 tests, ALL PASSING)
  • Test coverage includes:
    • Special values (±0, ±Inf, NaN)
    • Boundary values (max 65504, min normal 2^-14, min subnormal 2^-24)
    • Overflow/underflow behavior
    • Round-to-nearest ties-to-even
    • Arithmetic operations
    • Classification methods
    • Comparison semantics (bitwise & IEEE)

Why?

Support for half-precision floats is essential for:

  • Reduced payload size and memory footprint
  • ML/graphics interoperability where float16 is common
  • Cross-language compatibility with other Fory implementations

What does this PR do?

Adds production-ready IEEE 754 binary16 support to Rust runtime matching the exact specification in #3207, including:

  • Strong type safety (no raw u16 in public API)
  • Stable Rust only (no nightly features)
  • Zero external dependencies
  • Complete IEEE 754 compliance
  • Comprehensive test coverage

Related issues

Closes #3207

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?

    • Yes: Adds new public type float16 (exported as Float16)
    • New methods: from_bits, to_bits, from_f32, to_f32, classification methods, arithmetic methods
    • New constant values: ZERO, NEG_ZERO, INFINITY, NEG_INFINITY, NAN, MAX, MIN_POSITIVE, MIN_POSITIVE_SUBNORMAL
  • Does this PR introduce any binary protocol compatibility change?

    • Yes: Adds FLOAT16 (TypeId = 16) and FLOAT16_ARRAY (TypeId = 50) to wire format
    • Encoded as 2 bytes (little-endian u16 representing IEEE 754 binary16 bits)
    • Backward compatible: Existing code unaffected, new type opt-in only

Benchmark

Not applicable - this PR adds new functionality without modifying existing code paths. No performance impact on existing f32/f64 usage.

- Add float16 type as transparent wrapper around u16
- Implement IEEE 754 compliant f32<->float16 conversions
  - Round-to-nearest, ties-to-even rounding
  - Proper handling of NaN, Inf, ±0, subnormals
  - Overflow to infinity, underflow to subnormal/zero
- Add buffer read/write methods (write_f16, read_f16)
- Implement Serializer trait for float16
- Add float16 to all relevant type arrays and functions
- Implement arithmetic via f32 round-back
- Use Policy A (bitwise Eq/Hash, separate IEEE helpers)
- Add comprehensive unit tests (11 tests, all passing)

Resolves apache#3207
- Add #[allow(non_camel_case_types)] to float16 struct (per issue requirements)
- Add #[allow(dead_code)] to EXP_BIAS constant
- Add Apache license header to number.rs (required by CI)
- Suppress clippy::should_implement_trait warnings for explicit arithmetic methods
  (methods required by issue spec alongside trait implementations)
- Add #[allow(clippy::should_implement_trait)] to all arithmetic methods
- Restore missing float16 import in number.rs
- Fix formatting
@AshharAhmadKhan
Copy link
Contributor Author

hey @chaokunyang
I notice this PR now has merge conflicts with main. It appears FLOAT8 and BFLOAT16
support was added to main while I was working on FLOAT16.

Should I:

  1. Resolve conflicts by merging main and including all three float types?
  2. Rebase my branch on latest main?
  3. Wait for further direction?

Happy to resolve this whichever way you prefer.

@chaokunyang
Copy link
Collaborator

Hi @AshharAhmadKhan , I suggest to merge git main branch and only add support for FLOAT16 in this PR

@AshharAhmadKhan
Copy link
Contributor Author

Hi @chaokunyang,
I’ve merged the latest main and resolved conflicts, keeping only the FLOAT16 changes in this PR. All tests are passing.
Happy to make any further adjustments if needed.

- Add float16 type as transparent wrapper around u16
- Implement IEEE 754 compliant f32<->float16 conversions
  - Round-to-nearest, ties-to-even rounding
  - Proper handling of NaN, Inf, ±0, subnormals
  - Overflow to infinity, underflow to subnormal/zero
- Add buffer read/write methods (write_f16, read_f16)
- Implement Serializer trait for float16
- Add float16 to all relevant type arrays and functions
- Implement arithmetic via f32 round-back
- Use Policy A (bitwise Eq/Hash, separate IEEE helpers)
- Add comprehensive unit tests (11 tests, all passing)

Resolves apache#3207
@chaokunyang
Copy link
Collaborator

@AshharAhmadKhan Rust ci still failed, could you fix it first?
image

All 400+ tests passing. Clippy clean. Ready for CI review.
@AshharAhmadKhan
Copy link
Contributor Author

Hi @chaokunyang,

I've resolved all the Clippy warnings and formatting issues.

All 56 CI checks are now passing ✅, including the previously failing Rust CI checks on macOS and Ubuntu.

The implementation is ready for review. Please let me know if any additional changes are needed.

Thanks!

testfile.txt Outdated
@@ -0,0 +1 @@
test
Copy link
Collaborator

@chaokunyang chaokunyang Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is testfile.txt file necesary? why we need it?

}

pub static BASIC_TYPES: [TypeId; 33] = [
pub static BASIC_TYPES: [TypeId; 34] = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_primitive_type_id in rust/fory-core/src/serializer/list.rs also need chanage. Please also add tests for float16 array and float16 vec. and update array.rs and list.rs

@chaokunyang
Copy link
Collaborator

The float16 serializer is added, but key runtime integration paths are incomplete: dynamic-any resolution, skip/compatibility handling, and primitive array encoding are not wired for the new type. These cause real runtime failures and cross-language/type-id mismatches.

  • [P1] Register Float16 in builtin type resolver —rust/fory-core/src/serializer/number.rs:106-106
    This adds a Serializer for float16, but the builtin registration table is not updated, so Float16 has no runtime TypeInfo entry. Typed fields can
    work, but polymorphic paths (Box, Rc/Arc) fail with TypeId ... not found in type_info registry when the payload is Float16, which
    makes the new primitive unusable in existing dynamic-any flows.
  • [P1] Support skipping FLOAT16 in compatible deserialization — /Users/chaokunyang/Desktop/dev/fory/rust/fory-core/src/serializer/number.rs:121-123
    Now that TypeId::FLOAT16 is emitted, compatible/schema-evolution readers must be able to skip it, but serializer/skip.rs has no FLOAT16 handling.
    Deserializing older schemas from newer payloads that contain float16 values currently fails with Unimplemented type id: 17 instead of skipping the
    unknown field, which breaks backward-compatible reads.
  • [P1] Serialize Vec as FLOAT16_ARRAY —rust/fory-core/src/types.rs:401-401
    This declares Vec as FLOAT16_ARRAY, but the list primitive-path logic still omits float16, so Vec is serialized as generic LIST
    rather than FLOAT16_ARRAY. That causes type mismatches against peers that send/expect the xlang primitive array type and also loses the primitive-array fast path for this new type.

…h; add tests

- Register float16 and Vec<float16> in builtin type resolver (P1: fixes polymorphic Box/Rc/Arc paths)
- Add FLOAT16/FLOAT16_ARRAY cases to skip.rs (P1: fixes compatible/schema-evolution deserialization)
- Add FLOAT16 to get_primitive_type_id and is_primitive_type in list.rs (P1: Vec<float16> now serializes as FLOAT16_ARRAY not LIST)
- Add float16 Vec tests: basic round-trip, special values (Inf/NaN/subnormal), empty
- Add float16 array tests: basic round-trip, special values
- Remove testfile.txt

Addresses all P1 review comments from @chaokunyang
@chaokunyang
Copy link
Collaborator

chaokunyang commented Feb 20, 2026

@AshharAhmadKhan Please also update rust/fory-derive/src/object/util.rs to recognize float16 and float16 array when groups and sort fields and compute fingerprint. Other parts looks good to me now.

…type_id_by_name, and array type mapping in fory-derive util.rs
@AshharAhmadKhan
Copy link
Contributor Author

AshharAhmadKhan commented Feb 20, 2026

Hi @chaokunyang, I've addressed all your review comments:

  • ✅ Removed testfile.txt
  • ✅ Fixed list.rs get_primitive_type_id and is_primitive_type for float16
  • ✅ Wired all 3 P1 runtime paths: builtin resolver, skip.rs, and list primitive path
  • ✅ Updated fory-derive/src/object/util.rs to recognize float16 in PRIMITIVE_TYPE_NAMES, get_primitive_type_id, get_type_id_by_name, and array type mapping
  • ✅ Added Vec<float16> and [float16; N] tests

All Rust CI checks are passing

The one failing check (Python CI / windows-2022, 3.11) is unrelated to this PR — Chocolatey failed to download Bazel 8.2.1 due to a 504 Gateway Timeout from community.chocolatey.org (a transient infrastructure issue on the GitHub Actions runner). No code changes are involved.

Ready for approval!

@chaokunyang
Copy link
Collaborator

@AshharAhmadKhan Could you also add a struct test with flaot16 and float16 vec and flaot16 array fields, so we can ensure the fory-derive change does cover those cases

…ct struct test with float16 fields

- Add float16 special case in declare_var to use float16::ZERO instead of 0 as float16
- Add test_struct_with_float16_fields: ForyObject struct with scalar, Vec<float16>, [float16;3] fields
@AshharAhmadKhan
Copy link
Contributor Author

Hi @chaokunyang, I've added the ForyObject derive struct test with float16, Vec<float16>, and [float16; 3] fields as requested, and also fixed the declare_var codegen in fory-derive/src/object/read.rs to use float16::ZERO instead of 0 as float16 (which doesn't compile since float16 isn't a Rust primitive).

Copy link
Collaborator

@chaokunyang chaokunyang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@chaokunyang chaokunyang merged commit b050ba3 into apache:main Feb 21, 2026
56 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Rust] add float16 to rust

2 participants